Sensor Fusion for Kinetis MCUs (ISSDK/KSDK version)
precisionAccelerometer.c
Go to the documentation of this file.
1 // Copyright (c) 2014, 2015, 2016, NXP Semiconductors N.V.,
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above copyright
9 // notice, this list of conditions and the following disclaimer in the
10 // documentation and/or other materials provided with the distribution.
11 // * Neither the name of NXP Semiconductors N.V. nor the
12 // names of its contributors may be used to endorse or promote products
13 // derived from this software without specific prior written permission.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 // DISCLAIMED. IN NO EVENT SHALL NXP SEMICONDUCTORS N.V. BE LIABLE FOR ANY
19 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 #include <stdio.h>
26 #include "sensor_fusion.h"
27 #include "fusion.h"
28 
29 /*! \file precisionAccelerometer.c
30  \brief Implements accelerometer calibration routines
31 */
32 
33 // function resets the accelerometer buffer and accelerometer calibration
34 void fInitializeAccelCalibration(struct AccelCalibration *pthisAccelCal,
35  struct AccelBuffer *pthisAccelBuffer,
36  volatile int8 *AccelCalPacketOn)
37 {
38  float *pFlash; // pointer into flash memory
39  int8_t i,
40  j; // loop counters
41 
42  // set flags to false to denote no precision accelerometer measurements
43  pthisAccelBuffer->iStoreFlags = 0;
44 
45  // perform one transmission of the precision calibration (using invalid value MAX_ACCEL_CAL_ORIENTATIONS for calibration only)
46  *AccelCalPacketOn = MAX_ACCEL_CAL_ORIENTATIONS;
47 
48  // check to see if the stored accelerometer calibration has been erased
49  // the standard value for erased flash is 0xFF in each byte but for portability check against 0x12345678
50  pFlash = (float *) (CALIBRATION_NVM_ADDR + ACCEL_NVM_OFFSET);
51  if (*((uint32 *) pFlash++) == 0x12345678)
52  {
53  // a precision accelerometer calibration is present in flash
54  // copy accelerometer calibration elements (21x float total 84 bytes) from flash to RAM
55  for (i = CHX; i <= CHZ; i++) pthisAccelCal->fV[i] = *(pFlash++);
56  for (i = CHX; i <= CHZ; i++)
57  for (j = CHX; j <= CHZ; j++)
58  pthisAccelCal->finvW[i][j] = *(pFlash++);
59  for (i = CHX; i <= CHZ; i++)
60  for (j = CHX; j <= CHZ; j++)
61  pthisAccelCal->fR0[i][j] = *(pFlash++);
62  }
63  else
64  {
65  // flash has been erased and no accelerometer calibration is present
66  // initialize the precision accelerometer calibration in RAM to null default
67  pthisAccelCal->fV[CHX] = pthisAccelCal->fV[CHY] = pthisAccelCal->fV[CHZ] = 0.0F;
68  f3x3matrixAeqI(pthisAccelCal->finvW);
69  f3x3matrixAeqI(pthisAccelCal->fR0);
70  }
71 
72  // set current averaging location and counter to -1 for invalid
73  pthisAccelBuffer->iStoreLocation = pthisAccelBuffer->iStoreCounter = -1;
74 
75  return;
76 }
77 
78 void fUpdateAccelBuffer(struct AccelCalibration *pthisAccelCal,
79  struct AccelBuffer *pthisAccelBuffer,
80  struct AccelSensor *pthisAccel, volatile int8 *AccelCalPacketOn)
81 {
82  int16_t i; // loop counter
83 
84  // iStoreCounter > 0: precision measurements are still on-going
85  // iStoreCounter = 0: the precision measurement has just finished
86  // iStoreCounter = -1: no precision measurement is in progress
87  // check if a new precision measurement has started and zero sums if one has
88  if (pthisAccelBuffer->iStoreCounter == (ACCEL_CAL_AVERAGING_SECS * FUSION_HZ))
89  {
90  for (i = CHX; i <= CHZ; i++) pthisAccelBuffer->fSumGs[i] = 0.0F;
91  }
92 
93  // accumulate sum if averaging not yet complete
94  if (pthisAccelBuffer->iStoreCounter > 0)
95  {
96  for (i = CHX; i <= CHZ; i++)
97  pthisAccelBuffer->fSumGs[i] += pthisAccel->fGs[i];
98  pthisAccelBuffer->iStoreCounter--;
99  }
100 
101  // check if the measurement accumulation is complete and, if so, store average and set packet transmit flag
102  if (pthisAccelBuffer->iStoreCounter == 0)
103  {
104  // store the measurement, set the relevant flag and decrement the counter down to -1 denoting completion
105  for (i = CHX; i <= CHZ; i++)
106  {
107  pthisAccelBuffer->fGsStored[pthisAccelBuffer->iStoreLocation][i] =
108  pthisAccelBuffer->fSumGs[i] /
109  (float) (ACCEL_CAL_AVERAGING_SECS * FUSION_HZ);
110  }
111 
112  pthisAccelBuffer->iStoreFlags |=
113  (
114  1 <<
115  pthisAccelBuffer->iStoreLocation
116  );
117  pthisAccelBuffer->iStoreCounter--;
118 
119  // compute the new precision accelerometer calibration including rotation using all measurements
120  fRunAccelCalibration(pthisAccelCal, pthisAccelBuffer, pthisAccel);
121 
122  // and make one packet transmission of this measurement with the new calibration
123  *AccelCalPacketOn = pthisAccelBuffer->iStoreLocation;
124  }
125 
126  return;
127 }
128 
129 // function maps the accelerometer data fGs (g) onto precision calibrated and de-rotated data fGc (g), iGc (counts)
130 void fInvertAccelCal(struct AccelSensor *pthisAccel,
131  struct AccelCalibration *pthisAccelCal)
132 {
133  // local variables
134  float ftmp[3]; // temporary array
135  int8_t i; // loop counter
136 
137  //subtract the offset vector fV (g): ftmp[]=fGs[]-V[]
138  for (i = CHX; i <= CHZ; i++)
139  ftmp[i] = pthisAccel->fGs[i] - pthisAccelCal->fV[i];
140 
141  // apply the inverse rotation correction matrix finvW: fGc=inv(W)*(fGs[]-V[])
142  for (i = CHX; i <= CHZ; i++)
143  {
144  pthisAccel->fGc[i] = pthisAccelCal->finvW[i][CHX] *
145  ftmp[CHX] +
146  pthisAccelCal->finvW[i][CHY] *
147  ftmp[CHY] +
148  pthisAccelCal->finvW[i][CHZ] *
149  ftmp[CHZ];
150  }
151 
152  // apply the inverse of the forward rotation matrix fR0: fGc=inv(R).inv(W)*(fGs[]-V[])
153  for (i = CHX; i <= CHZ; i++) ftmp[i] = pthisAccel->fGc[i];
154  for (i = CHX; i <= CHZ; i++)
155  {
156  pthisAccel->fGc[i] = pthisAccelCal->fR0[CHX][i] *
157  ftmp[CHX] +
158  pthisAccelCal->fR0[CHY][i] *
159  ftmp[CHY] +
160  pthisAccelCal->fR0[CHZ][i] *
161  ftmp[CHZ];
162  pthisAccel->iGc[i] = (int16_t) (pthisAccel->fGc[i] * pthisAccel->iCountsPerg);
163  }
164 
165  return;
166 }
167 
168 // function runs the precision accelerometer calibration
169 void fRunAccelCalibration(struct AccelCalibration *pthisAccelCal,
170  struct AccelBuffer *pthisAccelBuffer,
171  struct AccelSensor *pthisAccel)
172 {
173  float fGc0[3]; // calibrated but not de-rotated measurement 0
174  uint8_t iMeasurements; // number of stored measurements
175  int8_t i; // loop counters
176 
177  // calculate how many measurements are present in the accelerometer measurement buffer
178  iMeasurements = 0;
179  for (i = 0; i < MAX_ACCEL_CAL_ORIENTATIONS; i++)
180  {
181  if (pthisAccelBuffer->iStoreFlags & (1 << i)) iMeasurements++;
182  }
183 
184  // perform the highest quality calibration possible given this number
185  if (iMeasurements >= 9)
186  {
187  // perform the 10 element calibration
188  fComputeAccelCalibration10(pthisAccelBuffer, pthisAccelCal, pthisAccel);
189  }
190  else if (iMeasurements >= 6)
191  {
192  // perform the 7 element calibration
193  fComputeAccelCalibration7(pthisAccelBuffer, pthisAccelCal, pthisAccel);
194  }
195  else if (iMeasurements >= 4)
196  {
197  // perform the 4 element calibration
198  fComputeAccelCalibration4(pthisAccelBuffer, pthisAccelCal, pthisAccel);
199  }
200 
201  // calculate the rotation correction matrix to rotate calibrated measurement 0 to flat
202  if (pthisAccelBuffer->iStoreFlags & 1)
203  {
204  // apply offset and gain calibration but not rotation to measurement 0 (flat) if present
205  // set ftmpA3x1 = invW . (fGs - fV)
206  for (i = CHX; i <= CHZ; i++)
207  fGc0[i] = pthisAccelBuffer->fGsStored[0][i] - pthisAccelCal->fV[i];
208  fVeq3x3AxV(fGc0, pthisAccelCal->finvW);
209 
210  // compute the new final rotation matrix if rotation 0 (flat) is present.
211  // multiplying by the transpose of this matrix therefore forces measurement zero to flat.
212  switch (THISCOORDSYSTEM)
213  {
214  case NED:
215  f3DOFTiltNED(pthisAccelCal->fR0, fGc0);
216  break;
217 
218  case ANDROID:
219  f3DOFTiltAndroid(pthisAccelCal->fR0, fGc0);
220  break;
221 
222  case WIN8:
223  default:
224  f3DOFTiltWin8(pthisAccelCal->fR0, fGc0);
225  break;
226  }
227  }
228 
229  return;
230 }
231 
232 // calculate the 4 element calibration from the available measurements
233 void fComputeAccelCalibration4(struct AccelBuffer *pthisAccelBuffer,
234  struct AccelCalibration *pthisAccelCal,
235  struct AccelSensor *pthisAccel)
236 {
237  int32 i,
238  j; // loop counters
239  float ftmp; // scratch
240  int8_t ierror; // flag from matrix inversion
241 
242  // working arrays for 4x4 matrix inversion
243  float *pfRows[4];
244  int8_t iColInd[4];
245  int8_t iRowInd[4];
246  int8_t iPivot[4];
247 
248  // zero the 4x4 matrix XTX (in upper left of fmatA) and 4x1 vector XTY (in upper fvecA)
249  for (i = 0; i < 4; i++)
250  {
251  pthisAccelCal->fvecA[i] = 0.0F;
252  for (j = 0; j < 4; j++)
253  {
254  pthisAccelCal->fmatA[i][j] = 0.0F;
255  }
256  }
257 
258  // accumulate fXTY (in fvecA) and fXTX4x4 (in fmatA)
259  for (i = 0; i < MAX_ACCEL_CAL_ORIENTATIONS; i++)
260  {
261  // accumulate vector X^T.Y if this entry is valid
262  if ((pthisAccelBuffer->iStoreFlags) & (1 << i))
263  {
264  ftmp = pthisAccelBuffer->fGsStored[i][CHX] *
265  pthisAccelBuffer->fGsStored[i][CHX] +
266  pthisAccelBuffer->fGsStored[i][CHY] *
267  pthisAccelBuffer->fGsStored[i][CHY] +
268  pthisAccelBuffer->fGsStored[i][CHZ] *
269  pthisAccelBuffer->fGsStored[i][CHZ];
270  pthisAccelCal->fvecA[0] += pthisAccelBuffer->fGsStored[i][CHX] * ftmp;
271  pthisAccelCal->fvecA[1] += pthisAccelBuffer->fGsStored[i][CHY] * ftmp;
272  pthisAccelCal->fvecA[2] += pthisAccelBuffer->fGsStored[i][CHZ] * ftmp;
273  pthisAccelCal->fvecA[3] += ftmp;
274 
275  // accumulate above diagonal terms of matrix X^T.X
276  pthisAccelCal->fmatA[CHX][CHX] += pthisAccelBuffer->fGsStored[i][
277  CHX] *
278  pthisAccelBuffer->fGsStored[i][CHX];
279  pthisAccelCal->fmatA[CHX][CHY] += pthisAccelBuffer->fGsStored[i][
280  CHX] *
281  pthisAccelBuffer->fGsStored[i][CHY];
282  pthisAccelCal->fmatA[CHX][CHZ] += pthisAccelBuffer->fGsStored[i][
283  CHX] *
284  pthisAccelBuffer->fGsStored[i][CHZ];
285  pthisAccelCal->fmatA[CHX][3] += pthisAccelBuffer->fGsStored[i][CHX];
286  pthisAccelCal->fmatA[CHY][CHY] += pthisAccelBuffer->fGsStored[i][
287  CHY] *
288  pthisAccelBuffer->fGsStored[i][CHY];
289  pthisAccelCal->fmatA[CHY][CHZ] += pthisAccelBuffer->fGsStored[i][
290  CHY] *
291  pthisAccelBuffer->fGsStored[i][CHZ];;
292  pthisAccelCal->fmatA[CHY][3] += pthisAccelBuffer->fGsStored[i][CHY];
293  pthisAccelCal->fmatA[CHZ][CHZ] += pthisAccelBuffer->fGsStored[i][
294  CHZ] *
295  pthisAccelBuffer->fGsStored[i][CHZ];;
296  pthisAccelCal->fmatA[CHZ][3] += pthisAccelBuffer->fGsStored[i][CHZ];
297  pthisAccelCal->fmatA[3][3] += 1.0F;
298  }
299  }
300 
301  // copy above diagonal elements of fXTX4x4 = X^T.X to below diagonal elements
302  pthisAccelCal->fmatA[CHY][CHX] = pthisAccelCal->fmatA[CHX][CHY];
303  pthisAccelCal->fmatA[CHZ][CHX] = pthisAccelCal->fmatA[CHX][CHZ];
304  pthisAccelCal->fmatA[CHZ][CHY] = pthisAccelCal->fmatA[CHY][CHZ];
305  pthisAccelCal->fmatA[3][CHX] = pthisAccelCal->fmatA[CHX][3];
306  pthisAccelCal->fmatA[3][CHY] = pthisAccelCal->fmatA[CHY][3];
307  pthisAccelCal->fmatA[3][CHZ] = pthisAccelCal->fmatA[CHZ][3];
308 
309  // calculate in situ inverse of X^T.X
310  for (i = 0; i < 4; i++)
311  {
312  pfRows[i] = pthisAccelCal->fmatA[i];
313  }
314 
315  fmatrixAeqInvA(pfRows, iColInd, iRowInd, iPivot, 4, &ierror);
316 
317  // calculate the solution vector fvecB = inv(X^T.X).X^T.Y
318  for (i = 0; i < 4; i++)
319  {
320  pthisAccelCal->fvecB[i] = 0.0F;
321  for (j = 0; j < 4; j++)
322  {
323  pthisAccelCal->fvecB[i] += pthisAccelCal->fmatA[i][j] * pthisAccelCal->fvecA[j];
324  }
325  }
326 
327  // extract the offset vector
328  pthisAccelCal->fV[CHX] = 0.5F * pthisAccelCal->fvecB[CHX];
329  pthisAccelCal->fV[CHY] = 0.5F * pthisAccelCal->fvecB[CHY];
330  pthisAccelCal->fV[CHZ] = 0.5F * pthisAccelCal->fvecB[CHZ];
331 
332  // set ftmp to 1/W where W is the forward gain to fit the 1g sphere
333  ftmp = 1.0F / sqrtf(fabsf(pthisAccelCal->fvecB[3] + pthisAccelCal->fV[CHX] *
334  pthisAccelCal->fV[CHX] + pthisAccelCal->fV[CHY] *
335  pthisAccelCal->fV[CHY] + pthisAccelCal->fV[CHZ] *
336  pthisAccelCal->fV[CHZ]));
337 
338  // copy the inverse gain 1/W to the inverse gain matrix
339  pthisAccelCal->finvW[CHX][CHY] = pthisAccelCal->finvW[CHY][CHX] = 0.0F;
340  pthisAccelCal->finvW[CHX][CHZ] = pthisAccelCal->finvW[CHZ][CHX] = 0.0F;
341  pthisAccelCal->finvW[CHY][CHZ] = pthisAccelCal->finvW[CHZ][CHY] = 0.0F;
342  pthisAccelCal->finvW[CHX][CHX] = pthisAccelCal->finvW[CHY][CHY] = pthisAccelCal->finvW[CHZ][CHZ] = ftmp;
343 
344  return;
345 }
346 
347 // calculate the 7 element calibration from the available measurements
348 void fComputeAccelCalibration7(struct AccelBuffer *pthisAccelBuffer,
349  struct AccelCalibration *pthisAccelCal,
350  struct AccelSensor *pthisAccel)
351 {
352  int32 i,
353  j,
354  m,
355  n; // loop counters
356  float det; // matrix determinant
357  float fg0; // fitted local gravity magnitude
358 
359  // zero the on and above diagonal elements of the 7x7 symmetric measurement matrix fmatA
360  for (i = 0; i < 7; i++)
361  for (j = i; j < 7; j++) pthisAccelCal->fmatA[i][j] = 0.0F;
362 
363  // last entry of vector fvecA is always 1.0 so move assignment outside the loop
364  pthisAccelCal->fvecA[6] = 1.0F;
365 
366  // loop over all orientations
367  for (i = 0; i < MAX_ACCEL_CAL_ORIENTATIONS; i++)
368  {
369  // accumulate fvecA if this entry is valid
370  if ((pthisAccelBuffer->iStoreFlags) & (1 << i))
371  {
372  // compute the remaining members of the measurement vector fvecA
373  pthisAccelCal->fvecA[0] = pthisAccelBuffer->fGsStored[i][CHX] * pthisAccelBuffer->fGsStored[i][CHX];
374  pthisAccelCal->fvecA[1] = pthisAccelBuffer->fGsStored[i][CHY] * pthisAccelBuffer->fGsStored[i][CHY];
375  pthisAccelCal->fvecA[2] = pthisAccelBuffer->fGsStored[i][CHZ] * pthisAccelBuffer->fGsStored[i][CHZ];
376  pthisAccelCal->fvecA[3] = pthisAccelBuffer->fGsStored[i][CHX];
377  pthisAccelCal->fvecA[4] = pthisAccelBuffer->fGsStored[i][CHY];
378  pthisAccelCal->fvecA[5] = pthisAccelBuffer->fGsStored[i][CHZ];
379 
380  // accumulate the on-and above-diagonal terms of fmatA=Sigma{fvecA^T * fvecA}
381  for (m = 0; m < 7; m++)
382  for (n = m; n < 7; n++)
383  pthisAccelCal->fmatA[m][n] += pthisAccelCal->fvecA[m] * pthisAccelCal->fvecA[n];
384  } // end of test for stored data
385  } // end of loop over orientations
386 
387  // copy the above diagonal elements of symmetric product matrix fmatA to below the diagonal
388  for (m = 1; m < 7; m++)
389  for (n = 0; n < m; n++)
390  pthisAccelCal->fmatA[m][n] = pthisAccelCal->fmatA[n][m];
391 
392  // set fvecA to the unsorted eigenvalues and fmatB to the unsorted normalized eigenvectors of fmatA
393  fEigenCompute10(pthisAccelCal->fmatA, pthisAccelCal->fvecA,
394  pthisAccelCal->fmatB, 7);
395 
396  // set ellipsoid matrix A from elements of the solution vector column j with smallest eigenvalue
397  j = 0;
398  for (i = 1; i < 7; i++)
399  if (pthisAccelCal->fvecA[i] < pthisAccelCal->fvecA[j]) j = i;
400 
401  // negate the entire solution vector if ellipsoid matrix has negative determinant
402  det = pthisAccelCal->fmatB[0][j] *
403  pthisAccelCal->fmatB[1][j] *
404  pthisAccelCal->fmatB[2][j];
405  if (det < 0.0F)
406  {
407  for (i = 0; i < 7; i++)
408  {
409  pthisAccelCal->fmatB[i][j] = -pthisAccelCal->fmatB[i][j];
410  }
411  }
412 
413  // compute invW and V and fitted gravity g0 from solution vector j
414  f3x3matrixAeqScalar(pthisAccelCal->finvW, 0.0F);
415  pthisAccelCal->finvW[CHX][CHX] = sqrtf(fabsf(pthisAccelCal->fmatB[0][j]));
416  pthisAccelCal->finvW[CHY][CHY] = sqrtf(fabsf(pthisAccelCal->fmatB[1][j]));
417  pthisAccelCal->finvW[CHZ][CHZ] = sqrtf(fabsf(pthisAccelCal->fmatB[2][j]));
418  pthisAccelCal->fV[CHX] = -0.5F *
419  pthisAccelCal->fmatB[3][j] /
420  pthisAccelCal->fmatB[0][j];
421  pthisAccelCal->fV[CHY] = -0.5F *
422  pthisAccelCal->fmatB[4][j] /
423  pthisAccelCal->fmatB[1][j];
424  pthisAccelCal->fV[CHZ] = -0.5F *
425  pthisAccelCal->fmatB[5][j] /
426  pthisAccelCal->fmatB[2][j];
427  fg0 = sqrtf(fabsf(pthisAccelCal->fmatB[0][j] * pthisAccelCal->fV[CHX] *
428  pthisAccelCal->fV[CHX] + pthisAccelCal->fmatB[1][j] *
429  pthisAccelCal->fV[CHY] * pthisAccelCal->fV[CHY] +
430  pthisAccelCal->fmatB[2][j] * pthisAccelCal->fV[CHZ] *
431  pthisAccelCal->fV[CHZ] - pthisAccelCal->fmatB[6][j]));
432 
433  // normalize invW to fit the 1g sphere
434  pthisAccelCal->finvW[CHX][CHX] /= fg0;
435  pthisAccelCal->finvW[CHY][CHY] /= fg0;
436  pthisAccelCal->finvW[CHZ][CHZ] /= fg0;
437 
438  return;
439 }
440 
441 // calculate the 10 element calibration from the available measurements
442 void fComputeAccelCalibration10(struct AccelBuffer *pthisAccelBuffer,
443  struct AccelCalibration *pthisAccelCal,
444  struct AccelSensor *pthisAccel)
445 {
446  int32 i,
447  j,
448  k,
449  l,
450  m,
451  n; // loop counters
452  float det; // matrix determinant
453  float ftmp; // scratch
454  float fg0; // fitted local gravity magnitude
455 
456  // zero the on and above diagonal elements of the 10x10 symmetric measurement matrix fmatA
457  for (i = 0; i < 10; i++)
458  for (j = i; j < 10; j++) pthisAccelCal->fmatA[i][j] = 0.0F;
459 
460  // last entry of vector fvecA is always 1.0 so move assignment outside the loop
461  pthisAccelCal->fvecA[9] = 1.0F;
462 
463  // loop over all orientations
464  for (i = 0; i < MAX_ACCEL_CAL_ORIENTATIONS; i++)
465  {
466  // accumulate fvecA if this entry is valid
467  if ((pthisAccelBuffer->iStoreFlags) & (1 << i))
468  {
469  // compute the remaining members of the measurement vector fvecA
470  pthisAccelCal->fvecA[6] = pthisAccelBuffer->fGsStored[i][CHX];
471  pthisAccelCal->fvecA[7] = pthisAccelBuffer->fGsStored[i][CHY];
472  pthisAccelCal->fvecA[8] = pthisAccelBuffer->fGsStored[i][CHZ];
473  pthisAccelCal->fvecA[0] = pthisAccelCal->fvecA[6] * pthisAccelCal->fvecA[6];
474  pthisAccelCal->fvecA[1] = 2.0F *
475  pthisAccelCal->fvecA[6] *
476  pthisAccelCal->fvecA[7];
477  pthisAccelCal->fvecA[2] = 2.0F *
478  pthisAccelCal->fvecA[6] *
479  pthisAccelCal->fvecA[8];
480  pthisAccelCal->fvecA[3] = pthisAccelCal->fvecA[7] * pthisAccelCal->fvecA[7];
481  pthisAccelCal->fvecA[4] = 2.0F *
482  pthisAccelCal->fvecA[7] *
483  pthisAccelCal->fvecA[8];
484  pthisAccelCal->fvecA[5] = pthisAccelCal->fvecA[8] * pthisAccelCal->fvecA[8];
485 
486  // accumulate the on-and above-diagonal terms of fmatA=Sigma{fvecA^T * fvecA}
487  for (m = 0; m < 10; m++)
488  {
489  for (n = m; n < 10; n++)
490  {
491  pthisAccelCal->fmatA[m][n] += pthisAccelCal->fvecA[m] * pthisAccelCal->fvecA[n];
492  }
493  }
494  } // end of test for stored data
495  } // end of loop over orientations
496 
497  // copy the above diagonal elements of symmetric product matrix fmatA to below the diagonal
498  for (m = 1; m < 10; m++)
499  for (n = 0; n < m; n++)
500  pthisAccelCal->fmatA[m][n] = pthisAccelCal->fmatA[n][m];
501 
502  // set fvecA to the unsorted eigenvalues and fmatB to the unsorted normalized eigenvectors of fmatA
503  fEigenCompute10(pthisAccelCal->fmatA, pthisAccelCal->fvecA,
504  pthisAccelCal->fmatB, 10);
505 
506  // set ellipsoid matrix A from elements of the solution vector column j with smallest eigenvalue
507  j = 0;
508  for (i = 1; i < 10; i++)
509  {
510  if (pthisAccelCal->fvecA[i] < pthisAccelCal->fvecA[j])
511  {
512  j = i;
513  }
514  }
515 
516  pthisAccelCal->fA[0][0] = pthisAccelCal->fmatB[0][j];
517  pthisAccelCal->fA[0][1] = pthisAccelCal->fA[1][0] = pthisAccelCal->fmatB[1][j];
518  pthisAccelCal->fA[0][2] = pthisAccelCal->fA[2][0] = pthisAccelCal->fmatB[2][j];
519  pthisAccelCal->fA[1][1] = pthisAccelCal->fmatB[3][j];
520  pthisAccelCal->fA[1][2] = pthisAccelCal->fA[2][1] = pthisAccelCal->fmatB[4][j];
521  pthisAccelCal->fA[2][2] = pthisAccelCal->fmatB[5][j];
522 
523  // negate entire solution if A has negative determinant
524  det = f3x3matrixDetA(pthisAccelCal->fA);
525  if (det < 0.0F)
526  {
527  f3x3matrixAeqMinusA(pthisAccelCal->fA);
528  pthisAccelCal->fmatB[6][j] = -pthisAccelCal->fmatB[6][j];
529  pthisAccelCal->fmatB[7][j] = -pthisAccelCal->fmatB[7][j];
530  pthisAccelCal->fmatB[8][j] = -pthisAccelCal->fmatB[8][j];
531  pthisAccelCal->fmatB[9][j] = -pthisAccelCal->fmatB[9][j];
532  det = -det;
533  }
534 
535  // compute the inverse of the ellipsoid matrix
536  f3x3matrixAeqInvSymB(pthisAccelCal->finvA, pthisAccelCal->fA);
537 
538  // compute the offset vector V
539  for (l = CHX; l <= CHZ; l++)
540  {
541  pthisAccelCal->fV[l] = 0.0F;
542  for (m = CHX; m <= CHZ; m++)
543  {
544  pthisAccelCal->fV[l] += pthisAccelCal->finvA[l][m] * pthisAccelCal->fmatB[m + 6][j];
545  }
546 
547  pthisAccelCal->fV[l] *= -0.5F;
548  }
549 
550  // compute the local gravity fit to these calibration coefficients
551  fg0 = sqrtf(fabsf(pthisAccelCal->fA[0][0] * pthisAccelCal->fV[CHX] *
552  pthisAccelCal->fV[CHX] + 2.0F * pthisAccelCal->fA[0][1] *
553  pthisAccelCal->fV[CHX] * pthisAccelCal->fV[CHY] + 2.0F *
554  pthisAccelCal->fA[0][2] * pthisAccelCal->fV[CHX] *
555  pthisAccelCal->fV[CHZ] + pthisAccelCal->fA[1][1] *
556  pthisAccelCal->fV[CHY] * pthisAccelCal->fV[CHY] + 2.0F *
557  pthisAccelCal->fA[1][2] * pthisAccelCal->fV[CHY] *
558  pthisAccelCal->fV[CHZ] + pthisAccelCal->fA[2][2] *
559  pthisAccelCal->fV[CHZ] * pthisAccelCal->fV[CHZ] -
560  pthisAccelCal->fmatB[9][j]));
561 
562  // compute trial invW from the square root of fA
563  // set fvecA to the unsorted eigenvalues and fmatB to the unsorted eigenvectors of fmatA
564  // where fmatA holds the 3x3 matrix fA in its top left elements
565  for (i = 0; i < 3; i++)
566  for (j = 0; j < 3; j++)
567  pthisAccelCal->fmatA[i][j] = pthisAccelCal->fA[i][j];
568  fEigenCompute10(pthisAccelCal->fmatA, pthisAccelCal->fvecA,
569  pthisAccelCal->fmatB, 3);
570 
571  // set fmatB to be eigenvectors . diag(sqrt(sqrt(eigenvalues))) = fmatB . diag(sqrt(sqrt(fvecA))
572  for (j = 0; j < 3; j++) // loop over columns j
573  {
574  ftmp = sqrtf(sqrtf(fabsf(pthisAccelCal->fvecA[j])));
575  for (i = 0; i < 3; i++) // loop over rows i
576  {
577  pthisAccelCal->fmatB[i][j] *= ftmp;
578  }
579  }
580 
581  // set finvW to eigenvectors * diag(sqrt(eigenvalues)) * eigenvectors^T
582  // = fmatB * fmatB^T = sqrt(fA) (guaranteed symmetric)
583  // loop over rows
584  for (i = 0; i < 3; i++)
585  {
586  // loop over on and above diagonal columns
587  for (j = i; j < 3; j++)
588  {
589  pthisAccelCal->finvW[i][j] = 0.0F;
590 
591  // accumulate the matrix product
592  for (k = 0; k < 3; k++)
593  {
594  pthisAccelCal->finvW[i][j] += pthisAccelCal->fmatB[i][k] * pthisAccelCal->fmatB[j][k];
595  }
596 
597  // copy to below diagonal element
598  pthisAccelCal->finvW[j][i] = pthisAccelCal->finvW[i][j];
599  }
600  }
601 
602  // scale finvW so that the calibrated measurements fit on the 1g sphere
603  for (i = CHX; i <= CHZ; i++)
604  {
605  for (j = CHX; j <= CHZ; j++)
606  {
607  pthisAccelCal->finvW[i][j] /= fg0;
608  }
609  }
610 
611  return;
612 }
void fmatrixAeqInvA(float *A[], int8 iColInd[], int8 iRowInd[], int8 iPivot[], int8 isize, int8 *pierror)
function uses Gauss-Jordan elimination to compute the inverse of matrix A in situ on exit...
Definition: matrix.c:666
int16_t iStoreLocation
-1 for none, 0 to 11 for the 12 storage locations
void fComputeAccelCalibration7(struct AccelBuffer *pthisAccelBuffer, struct AccelCalibration *pthisAccelCal, struct AccelSensor *pthisAccel)
calculate the 7 element calibration from the available measurements
#define CHY
Used to access Y-channel entries in various data data structures.
Definition: sensor_fusion.h:77
void f3DOFTiltNED(float fR[][3], float fGc[])
Aerospace NED accelerometer 3DOF tilt function, computing rotation matrix fR.
int16_t iStoreCounter
number of remaining iterations at FUSION_HZ to average measurement
Lower level sensor fusion interface.
void fComputeAccelCalibration4(struct AccelBuffer *pthisAccelBuffer, struct AccelCalibration *pthisAccelCal, struct AccelSensor *pthisAccel)
calculate the 4 element calibration from the available measurements
float fvecA[10]
scratch 10x1 vector used by calibration algorithms
float fvecB[4]
scratch 4x1 vector used by calibration algorithms
float fGsStored[MAX_ACCEL_CAL_ORIENTATIONS][3]
uncalibrated accelerometer measurements (g)
float fV[3]
offset vector (g)
void f3x3matrixAeqMinusA(float A[][3])
function negates all elements of 3x3 matrix A
Definition: matrix.c:147
#define ANDROID
identifier for Android axes and angles
int16_t iStoreFlags
denotes which measurements are present
float f3x3matrixDetA(float A[][3])
function calculates the determinant of a 3x3 matrix
Definition: matrix.c:209
int32_t int32
Definition: sensor_fusion.h:57
int16_t iCountsPerg
counts per g
void fUpdateAccelBuffer(struct AccelCalibration *pthisAccelCal, struct AccelBuffer *pthisAccelBuffer, struct AccelSensor *pthisAccel, volatile int8 *AccelCalPacketOn)
Update the buffer used to store samples used for accelerometer calibration.
The AccelSensor structure stores raw and processed measurements for a 3-axis accelerometer.
uint32_t uint32
Definition: sensor_fusion.h:60
void fComputeAccelCalibration10(struct AccelBuffer *pthisAccelBuffer, struct AccelCalibration *pthisAccelCal, struct AccelSensor *pthisAccel)
calculate the 10 element calibration from the available measurements
#define NED
identifier for NED (Aerospace) axes and angles
void fEigenCompute10(float A[][10], float eigval[], float eigvec[][10], int8 n)
function computes all eigenvalues and eigenvectors of a real symmetric matrix A[0..n-1][0..n-1] stored in the top left of a 10x10 array A[10][10]
Definition: matrix.c:234
void fRunAccelCalibration(struct AccelCalibration *pthisAccelCal, struct AccelBuffer *pthisAccelBuffer, struct AccelSensor *pthisAccel)
function runs the precision accelerometer calibration
float fGc[3]
averaged precision calibrated measurement (g)
The sensor_fusion.h file implements the top level programming interface.
void fVeq3x3AxV(float V[3], float A[][3])
function multiplies the 3x1 vector V by a 3x3 matrix A
Definition: matrix.c:871
#define CHZ
void f3x3matrixAeqInvSymB(float A[][3], float B[][3])
function directly calculates the symmetric inverse of a symmetric 3x3 matrix only the on and above di...
Definition: matrix.c:168
float finvA[3][3]
inverse of the ellipsoid matrix A
#define WIN8
identifier for Windows 8 axes and angles
accelerometer measurement buffer
void f3DOFTiltAndroid(float fR[][3], float fGc[])
Android accelerometer 3DOF tilt function computing, rotation matrix fR.
#define MAX_ACCEL_CAL_ORIENTATIONS
number of stored precision accelerometer measurements
float fmatA[10][10]
scratch 10x10 matrix used by calibration algorithms
#define CHX
Used to access X-channel entries in various data data structures.
Definition: sensor_fusion.h:76
float fmatB[10][10]
scratch 10x10 matrix used by calibration algorithms
void f3x3matrixAeqI(float A[][3])
function sets the 3x3 matrix A to the identity matrix
Definition: matrix.c:45
int16_t iGc[3]
averaged precision calibrated measurement (counts)
float fR0[3][3]
forward rotation matrix for measurement 0
float fGs[3]
averaged measurement (g)
#define THISCOORDSYSTEM
#define FUSION_HZ
(int) actual rate of fusion algorithm execution and sensor FIFO reads
void fInvertAccelCal(struct AccelSensor *pthisAccel, struct AccelCalibration *pthisAccelCal)
function maps the accelerometer data fGs (g) onto precision calibrated and de-rotated data fGc (g)...
void fInitializeAccelCalibration(struct AccelCalibration *pthisAccelCal, struct AccelBuffer *pthisAccelBuffer, volatile int8 *AccelCalPacketOn)
Initialize the accelerometer calibration functions.
float finvW[3][3]
inverse gain matrix
void f3x3matrixAeqScalar(float A[][3], float Scalar)
function sets every entry in the 3x3 matrix A to a constant scalar
Definition: matrix.c:109
float fSumGs[3]
averaging sum for current storage location
void f3DOFTiltWin8(float fR[][3], float fGc[])
Windows 8 accelerometer 3DOF tilt function computing, rotation matrix fR.
#define ACCEL_CAL_AVERAGING_SECS
calibration constants
precision accelerometer calibration structure
int8_t int8
Definition: sensor_fusion.h:55
float fA[3][3]
ellipsoid matrix A